package com.party.admin.biz.crowdfund;

import com.google.common.collect.Lists;
import com.party.admin.web.dto.output.crowdfund.DetailCountTableOutput;
import com.party.common.utils.DateUtils;
import com.party.common.utils.StringUtils;
import com.party.core.model.crowdfund.CrowdfundEvent;
import com.party.core.model.crowdfund.ProjectDetailCountInput;
import com.party.core.model.crowdfund.ProjectDetailCountOutput;
import com.party.core.service.crowdfund.ICrowdfundEventService;
import com.party.core.service.crowdfund.IProjectDetailCountService;
import com.party.core.service.crowdfund.IProjectService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @Description:
 * @Author: yangshoukun
 * @Date: 2018/7/10 15:56
 */
@Service
public class CrowdfundDetailCountBizService {
    private static Logger logger = LoggerFactory.getLogger(CrowdfundDetailCountBizService.class);

    @Autowired
    private IProjectDetailCountService projectDetailCountService;

    @Autowired
    private ICrowdfundEventService crowdfundEventService;

    @Autowired
    private IProjectService projectService;

    public List<ProjectDetailCountOutput> getCountByEvent(ProjectDetailCountInput input, String pattern, String datePattern) throws ParseException {
        Date start = DateUtils.parse(input.getApplyDateStart(), pattern);
        Date end = DateUtils.parse(input.getApplyDateEnd(), pattern);
        int timeLong = DateUtils.diffDate(end, start) + 1;

        List<ProjectDetailCountOutput> list = Lists.newArrayList();

        for (int i = 0; i < input.getPeriod(); i++) {
            String newStart = DateUtils.formatDate(DateUtils.addDay(start, timeLong * i), pattern);
            String newEnd = DateUtils.formatDate(DateUtils.addDay(end, timeLong * i), pattern);
            input.setApplyDateStart(newStart);
            input.setApplyDateEnd(newEnd);

            ProjectDetailCountOutput output = projectDetailCountService.getCountByEvent(input);
            calcOutput(output);
            // 时间
            output.setPeriodDate(newStart.substring(0, datePattern.length()).concat("~").concat(newEnd.substring(0, datePattern.length())));

            list.add(output);
        }
        return list;
    }

    /**
     * 众筹事件的数据报表
     * @param eventId
     * @return
     */
    public void eventDataTable(String eventId, ModelAndView mv) throws ParseException {
        if (StringUtils.isEmpty(eventId)) {
            return;
        }
        CrowdfundEvent crowdfundEvent = crowdfundEventService.get(eventId);
        List<Month> months = dealTime(crowdfundEvent.getCreateDate(), crowdfundEvent.getEndTime());
        if (CollectionUtils.isEmpty(months)) {
            return;
        }
        // 累计数据
        List<DetailCountTableOutput> accumulateList = findAndCalc(months, eventId, true, null, null);
        // 新增数据
        List<DetailCountTableOutput> appendList = findAndCalc(months, eventId, false, null, null);
        // 三天内
        List<DetailCountTableOutput> threeDayList = findAndCalc(months, eventId, false, 0, 2);
        // 七天
        List<DetailCountTableOutput> sevenDayList = findAndCalc(months, eventId, false, 0, 6);
        // 十五天
        List<DetailCountTableOutput> fifteenDayList = findAndCalc(months, eventId, false, 0, 14);

        // 计算总列数
        int i = 0;
        for (Month month : months) {
            for (Week week : month.getWeeks()) {
                i++;
            }
        }
        // 汇总数据
        // 累计数据汇总
        List<DetailCountTableOutput.DetailCountTableColumn> tempColumnList;
        ProjectDetailCountOutput accumulateCollect = new ProjectDetailCountOutput();
        out:
        for (int m = accumulateList.size() - 1; m >= 0; m--) {
            tempColumnList = accumulateList.get(m).getDetailCountTableColumnList();
            for (int n = tempColumnList.size() - 1; n >= 0; n--) {
                accumulateCollect = tempColumnList.get(n).getProjectDetailCountOutput();
                if (null != accumulateCollect.getApplyNum()) {
                    break out;
                }
            }
        }
        dealNull(accumulateCollect);
        // 新增数据
        ProjectDetailCountOutput appendCollect = calcCollect(appendList);
        // 三天内
        ProjectDetailCountOutput threeDayCollect = calcCollect(threeDayList);
        // 7天
        ProjectDetailCountOutput sevenDayCollect = calcCollect(sevenDayList);
        // 十五天
        ProjectDetailCountOutput fifteenDayCollect = calcCollect(fifteenDayList);

        mv.addObject("accumulateList", accumulateList);
        mv.addObject("appendList", appendList);
        mv.addObject("threeDayList", threeDayList);
        mv.addObject("sevenDayList", sevenDayList);
        mv.addObject("fifteenDayList", fifteenDayList);
        mv.addObject("accumulateCollect", accumulateCollect);
        mv.addObject("appendCollect", appendCollect);
        mv.addObject("threeDayCollect", threeDayCollect);
        mv.addObject("sevenDayCollect", sevenDayCollect);
        mv.addObject("fifteenDayCollect", fifteenDayCollect);
        mv.addObject("event", crowdfundEvent);
        mv.addObject("columnNum", i);
    }

    /**
     * 计算汇总数据
     * @param list
     * @return
     */
    private ProjectDetailCountOutput calcCollect(List<DetailCountTableOutput> list) {
        ProjectDetailCountOutput temp;
        ProjectDetailCountOutput result = new ProjectDetailCountOutput();
        dealNull(result);
        for (DetailCountTableOutput output : list) {
            for (DetailCountTableOutput.DetailCountTableColumn column : output.getDetailCountTableColumnList()) {
                temp = NewOutput(column.getProjectDetailCountOutput());
                // 报名人数
                result.setApplyNum(result.getApplyNum() + temp.getApplyNum());
                // 成功人数
                result.setSuccessNum(result.getSuccessNum() + temp.getSuccessNum());
                // 金额
                result.setMoney(result.getMoney() + temp.getMoney());
                // 成功金额
                result.setSuccessMoney(result.getSuccessMoney() + temp.getSuccessMoney());
            }
        }
        calcOutput(result);
        return result;
    }

    /**
     * 查询结果, 并计算
     * @param months
     * @param eventId
     * @param accumulate
     * @param qureyDayStart
     * @param qureyDayEnd
     * @return
     */
    private List<DetailCountTableOutput> findAndCalc(List<Month> months, String eventId, boolean accumulate,
                                                     Integer qureyDayStart, Integer qureyDayEnd) {
         Date now = new Date();
        final String pattern = "yyyy-MM-dd";
        List<DetailCountTableOutput> list = Lists.newArrayList();
        Date monthStart = months.get(0).getStartDate();
        int i = 0;
        for (Month month: months) {
            DetailCountTableOutput tempOutput = new DetailCountTableOutput();
            tempOutput.setMonth(month.getMonth());

            List<DetailCountTableOutput.DetailCountTableColumn> DetailCountTableColumnList = tempOutput.getDetailCountTableColumnList();

            DetailCountTableOutput.DetailCountTableColumn tempColumn;
            ProjectDetailCountOutput tempProjectOutput;
            ProjectDetailCountInput input = new ProjectDetailCountInput();
            input.setEventId(eventId);
            // 时间段不为空
            if (null != qureyDayStart) {
                input.setQureyDayStart(qureyDayStart);
                input.setQureyDayEnd(qureyDayEnd);
            }
            // 累计数据, 开始查询时间都是事件创建时间
            if (accumulate) {
                input.setApplyDateStart(DateUtils.formatDate(monthStart));
                input.setEventCreatDate(DateUtils.formatDate(monthStart));
            }
            List<Week> weeks = month.getWeeks();

            for (Week week: weeks) {
                tempColumn = new DetailCountTableOutput.DetailCountTableColumn();
                // 非累计数据, 开始查询时间为本周开始时间
                if (!accumulate) {
                    input.setApplyDateStart(DateUtils.formatDate(week.getStartDate()));
                    input.setEventCreatDate(DateUtils.formatDate(monthStart));
                }
                input.setApplyDateEnd(DateUtils.formatDate(week.getEndDate()));

                // 如果时间大于现在, 就不需要数据
                if (week.getStartDate().compareTo(now) > 0) {
                    tempProjectOutput = new ProjectDetailCountOutput();
                } else {
                    // 时间段是否为空, 查询不同的表
                    if (null == qureyDayStart) {
                        // 这里处理众筹转移来的数据u
                        if (!accumulate && i != 0) {
                            input.setEventCreatDate(null);
                        }
                        tempProjectOutput = projectService.getDataTable(input);
                    } else {
                        input.setApplyDateStart(input.getApplyDateStart().concat(" 00:00:00"));
                        input.setApplyDateEnd(input.getApplyDateEnd().concat(" 23:59:59"));
                        tempProjectOutput = projectDetailCountService.getCountByEvent(input);
                    }
                }
                tempColumn.setTitle(week.getWeek().concat("(").concat(DateUtils.formatDate(week.getStartDate(), pattern))
                        .concat("~").concat(DateUtils.formatDate(week.getEndDate(), pattern)).concat(")"));
                tempColumn.setProjectDetailCountOutput(tempProjectOutput);
                // 计算人数成功率和金额成功率
                calcOutput(tempProjectOutput);
                DetailCountTableColumnList.add(tempColumn);
                i++;
            }
            list.add(tempOutput);
        }
        return list;
    }

    /**
     * 计算*率
     * @param output
     */
    private void calcOutput(ProjectDetailCountOutput output) {
        if (null == output.getApplyNum()) {
            return;
        }
        dealNull(output);
        Integer applyNum = output.getApplyNum();
        Double money = output.getMoney();
        Integer orderNum = output.getOrderNum();
        Integer successNum = output.getSuccessNum();
        Double successMoney= output.getSuccessMoney();

        // 计算
        int denominator = applyNum < 1 ? 1 : applyNum;
        // 平均金额
        String avgMoney = new DecimalFormat("0.00").format(money / denominator);
        // 平均订单
        int avgOrderNum = orderNum / denominator;
        DecimalFormat format = new DecimalFormat("#.##");
        // 报名成功率
        String successNumRate = format.format(successNum * 100 / denominator).concat("%");
        // 金额成功率
        double denominatorMoney = money.equals(Double.valueOf(0)) ? 1 : money;
        String successMoneyRate = format.format(successMoney * 100 / denominatorMoney).concat("%");

        output.setAverageOrderNum(avgOrderNum);
        output.setAverageMoney(Double.parseDouble(avgMoney));
        output.setSuccessNumRate(successNumRate);
        output.setSuccessMoneyRate(successMoneyRate);
    }

    /**
     * 处理空值
     * @param output
     */
    private void dealNull(ProjectDetailCountOutput output) {
        Integer applyNum = null == output.getApplyNum() ? 0 : output.getApplyNum();
        Double money = null == output.getMoney() ? 0 : output.getMoney();
        Integer orderNum = null == output.getOrderNum() ? 0 : output.getOrderNum();
        Integer successNum = null == output.getSuccessNum() ? 0 : output.getSuccessNum();
        Double successMoney= null == output.getSuccessMoney() ? 0 : output.getSuccessMoney();

        DecimalFormat format = new DecimalFormat("#.##");
        output.setApplyNum(applyNum);
        output.setMoney(Double.parseDouble(format.format(money)));
        output.setOrderNum(orderNum);
        output.setSuccessNum(successNum);
        output.setSuccessMoney(Double.parseDouble(format.format(successMoney)));
    }

    /**
     * 返回新的output, 不对原bean做处理
     * @param output
     * @return
     */
    private ProjectDetailCountOutput NewOutput(ProjectDetailCountOutput output) {
        Integer applyNum = null == output.getApplyNum() ? 0 : output.getApplyNum();
        Double money = null == output.getMoney() ? 0 : output.getMoney();
        Integer orderNum = null == output.getOrderNum() ? 0 : output.getOrderNum();
        Integer successNum = null == output.getSuccessNum() ? 0 : output.getSuccessNum();
        Double successMoney= null == output.getSuccessMoney() ? 0 : output.getSuccessMoney();

        ProjectDetailCountOutput newOutput = new ProjectDetailCountOutput();
        BeanUtils.copyProperties(output, newOutput);
        DecimalFormat format = new DecimalFormat("#.##");
        newOutput.setApplyNum(applyNum);
        newOutput.setMoney(Double.parseDouble(format.format(money)));
        newOutput.setOrderNum(orderNum);
        newOutput.setSuccessNum(successNum);
        newOutput.setSuccessMoney(Double.parseDouble(format.format(successMoney)));
        return newOutput;
    }

    /**
     * 将时间转为类
     * @param startParam
     * @param endParam
     * @return
     * @throws ParseException
     */
    private List<Month> dealTime(Date startParam, Date endParam) throws ParseException {
        List<Month> list = Lists.newArrayList();

        final String pattern = "yyyy-MM-dd";
        final String patternTime = pattern + " 00:00:00";
        Date startTemp = DateUtils.parse(DateUtils.formatDate(startParam, patternTime), patternTime);
        Date mondayOfWeek = DateUtils.getFirstDayOfThisWeek(startTemp);
        //获取本周的最后一天
        Date mondayOfMonth = DateUtils.getFirstMondayOfThisMonth(startTemp);
        int mondayDiff = DateUtils.diffDate(mondayOfWeek, mondayOfMonth);

        Date startDate = mondayDiff > 0 ? mondayOfWeek : mondayOfMonth;

        /*Date now = new Date();
        boolean nowIsSmall = now.compareTo(endParam) < 0;
        Date endDate = nowIsSmall ? now : endParam;*/
        Date endDate = DateUtils.getLastDayOfThisWeek(DateUtils.parse(DateUtils.formatDate(endParam, patternTime), patternTime));

        int monthI = -1;

        Month month = null;
        Calendar calendar = Calendar.getInstance();
        while (startDate.compareTo(endDate) < 0) {
            int tempMonthI = DateUtils.getMonthNumOfYear(startDate);
            if (monthI != tempMonthI) {
                month = new Month();
                month.setMonth(tempMonthI + "月");
                if (mondayDiff < 0) {
                    month.setStartDate(startParam);
                    monthI = tempMonthI;
                    list.add(month);
                }else {
                    month.setStartDate(startDate);
                    monthI = tempMonthI;
                    list.add(month);
                }
            }

            Date sunday = DateUtils.addDay(startDate, 6);
            calendar.setTime(sunday);
            calendar.set(Calendar.HOUR, 23);
            calendar.set(Calendar.MINUTE, 59);
            calendar.set(Calendar.SECOND, 59);
            sunday = calendar.getTime();

            month.setEndDate(sunday);

            List<Week> weeks = month.getWeeks();

            Week week = new Week();
            week.setStartDate(startDate);
            week.setEndDate(sunday);
            week.setWeek("第" + DateUtils.getWeekNumOfMonth(startDate) + "周");

            weeks.add(week);

            startDate = DateUtils.addDay(startDate, 7);
        }
        // 处理第一天  是主题的创建时间
        // 本月周一大于本周周一, 这种情况要特殊处理
        List<Week> firstMonthWeeks = list.get(0).getWeeks();
        if (mondayDiff < 0) {
            Week firstWeek = new Week();
            firstWeek.setStartDate(startParam);
            firstWeek.setEndDate(DateUtils.getLastDayOfThisWeek(startParam));
            firstWeek.setWeek("第1周");
            firstMonthWeeks.add(0, firstWeek);
        } else {
            firstMonthWeeks.get(0).setStartDate(startParam);
        }
        // 最后一天 改为报名结束时间
        Month lastMonth = list.get(list.size() - 1);
        Week lastWeek = lastMonth.getWeeks().get(lastMonth.getWeeks().size() - 1);
//        if (nowIsSmall) {
//            lastWeek.setEndDate(now);
//        } else {
            lastWeek.setEndDate(endParam);
//        }
        return list;
    }

    private class Month {
        private String month;
        private Date startDate;
        private Date endDate;
        private List<Week> weeks = Lists.newArrayList();

        public Date getStartDate() {
            return startDate;
        }

        public void setStartDate(Date startDate) {
            this.startDate = startDate;
        }

        public Date getEndDate() {
            return endDate;
        }

        public void setEndDate(Date endDate) {
            this.endDate = endDate;
        }

        public String getMonth() {
            return month;
        }

        public void setMonth(String month) {
            this.month = month;
        }

        public List<Week> getWeeks() {
            return weeks;
        }

        public void setWeeks(List<Week> weeks) {
            this.weeks = weeks;
        }
    }

    private class Week {
        private String week;
        private Date startDate;
        private Date endDate;

        public String getWeek() {
            return week;
        }

        public void setWeek(String week) {
            this.week = week;
        }

        public Date getStartDate() {
            return startDate;
        }

        public void setStartDate(Date startDate) {
            this.startDate = startDate;
        }

        public Date getEndDate() {
            return endDate;
        }

        public void setEndDate(Date endDate) {
            this.endDate = endDate;
        }
    }
}
